feat: Add configurable AWS credential validation at bootstrap#6629
Conversation
|
Maybe a better configuration experience could be |
…tion - Add validate_at_bootstrap boolean field to AwsSecretManagerConfiguration - Default value is true (safe-by-default - validates credentials at bootstrap) - When set to false, secret retrieval is deferred until first access - Implement lazy-loading logic in AwsSecretsSupplier for deferred secrets - Add comprehensive unit and integration tests - Maintain backward compatibility (default behavior unchanged) This allows users to disable credential validation per-secret when credentials are not available at bootstrap time, while maintaining fail-fast behavior by default for production safety. Resolves issue where DataPrepper fails to start with invalid credentials by providing per-secret control over bootstrap validation. Signed-off-by: Sumit Bhattacharya <sumit4739@gmail.com>
ee9d46a to
227c332
Compare
dlvenable
left a comment
There was a problem hiding this comment.
Thanks for this change @BhattacharyaSumit !
| class AwsSecretsSupplierLazyLoadTest { | ||
|
|
||
| private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); | ||
| private static final String TEST_SECRET_ID = "test-secret"; |
There was a problem hiding this comment.
Use random values and initialize them in @BeforeEach. Make them member variables instead of static.
| private boolean disableRefresh = false; | ||
|
|
||
| @JsonProperty("validate_at_bootstrap") | ||
| private boolean validateAtBootstrap = true; // Default: true (safe-by-default) |
There was a problem hiding this comment.
We generally make our boolean configurations false by default. This is a convention offered to end-users to help with expectations.
Also we don't use the term bootstrap much.
I'd tend to think these are better names:
skip_initial_validationskip_validationskip_validation_on_start
|
|
||
| // Check if validation at bootstrap is disabled for this secret | ||
| if (!awsSecretManagerConfiguration.isValidateAtBootstrap()) { | ||
| LOG.info("Skipping secret retrieval at bootstrap for secret: {} (validate_at_bootstrap=false)", |
| // Check if secret was skipped at bootstrap and needs to be loaded now | ||
| Object keyValuePairs = secretIdToValue.get(secretId); | ||
| if (keyValuePairs == NOT_LOADED_SENTINEL) { | ||
| LOG.info("Secret {} was not loaded at bootstrap, loading now on first access...", secretId); |
There was a problem hiding this comment.
Do not use ellipses like this. Just end the sentence with a period.
| // Check if secret was skipped at bootstrap and needs to be loaded now | ||
| Object secretValue = secretIdToValue.get(secretId); | ||
| if (secretValue == NOT_LOADED_SENTINEL) { | ||
| LOG.info("Secret {} was not loaded at bootstrap, loading now on first access...", secretId); |
There was a problem hiding this comment.
Similar comment about ellipses.
| } | ||
|
|
||
| // Check if secret was skipped at bootstrap and needs to be loaded now | ||
| Object secretValue = secretIdToValue.get(secretId); |
There was a problem hiding this comment.
This is duplicate code. Consolidate it with the code above.
| final AwsSecretManagerConfiguration config = new AwsSecretManagerConfiguration(); | ||
|
|
||
| // Default should be true (safe-by-default) | ||
| assertThat(config.isValidateAtBootstrap(), equalTo(true)); |
There was a problem hiding this comment.
You won't need this with the updated default of false.
| @BeforeEach | ||
| void setUp() throws JsonProcessingException { | ||
| when(awsSecretManagerConfiguration.createGetSecretValueRequest()).thenReturn(getSecretValueRequest); | ||
| when(awsSecretManagerConfiguration.isValidateAtBootstrap()).thenReturn(true); // Default: validate at bootstrap |
There was a problem hiding this comment.
You also won't need this with the updated default.
|
Also, one header violation. Missing license header: examples/config/data-prepper-config-secret-validation.yaml |
b4c9b05 to
803e4fd
Compare
- Rename validate_at_bootstrap to skip_validation_on_start - Change default from true to false (validate by default per DataPrepper convention) - Remove ellipses from log messages - Extract duplicate lazy-loading code into loadSecretIfNeeded() helper method - Update tests to use random values as member variables initialized in @beforeeach - Update all test mocks to use new method name - Remove example configuration file - Remove accidentally committed build artifacts Signed-off-by: Sumit Bhattacharya <sumit4739@gmail.com>
803e4fd to
7360f59
Compare
dlvenable
left a comment
There was a problem hiding this comment.
Thank you @BhattacharyaSumit !
| private Object loadSecretIfNeeded(String secretId) { | ||
| Object value = secretIdToValue.get(secretId); | ||
| if (value == NOT_LOADED_SENTINEL) { | ||
| LOG.info("Secret {} was not loaded on start, loading now on first access.", secretId); | ||
| refresh(secretId); | ||
| value = secretIdToValue.get(secretId); | ||
| } | ||
| return value; |
There was a problem hiding this comment.
secretIdToValue is ConcurrentMap, but the check and refresh sequence here is not atomic. We should consider using compute() method or maybe put the sentinel check inside refresh method's compute() call.
| } else { | ||
| //This store is not a key value pair store. It is just a value store. | ||
| //If we are here, either KeyToUpdate passed is null or we simply ignore it and just put value in the store | ||
| secretIdToValue.put(secretId, newValue); |
There was a problem hiding this comment.
We may need to call loadSecretIfNeeded at the beginning of this updateValue method as well, otherwise there's chance that this else block will be executed if secret is not loaded yet (because NOT_LOADED_SENTINEL is not a Map), which will lead to overwriting the sentinel with newValue.
…unloaded secrets Use ConcurrentMap.compute() in loadSecretIfNeeded() to ensure the sentinel check and secret retrieval are performed atomically, avoiding race conditions with concurrent access. Add loadSecretIfNeeded() call at the beginning of updateValue() to ensure secrets are loaded before update logic runs, preventing the NOT_LOADED_SENTINEL from being treated as a plain value store. Signed-off-by: Sumit Bhattacharya <sumit4739@gmail.com>
oeyh
left a comment
There was a problem hiding this comment.
Thanks @BhattacharyaSumit !
Description
Adds a
skip_validation_on_startboolean field to AWS Secrets Manager configuration, allowing per-secret control over credential validation at DataPrepper bootstrap.Motivation
DataPrepper currently fails to start if AWS credentials for secrets are invalid at bootstrap, with no way to defer validation. This blocks deployment scenarios where credentials may not be available at startup but will be provided later during runtime.
Changes
skip_validation_on_startfield toAwsSecretManagerConfiguration(default:false)AwsSecretsSupplierfor secrets with validation disabledskip_validation_on_start=trueare loaded on first access instead of at bootstrapConfiguration Example
Issues Resolved
Resolves #[Issue number to be closed when this PR is merged]
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.